#ifndef _AMERICO_INTERSECTION_2D_H_
#define _AMERICO_INTERSECTION_2D_H_

#include <Vector.h>
#include <line.h>
#include <linevolume.h>

namespace Intersection
{
	namespace I2D
	{
		template <typename T>
		class Rectangle;

		template <typename T>
		class Circle 
		{
		public:
			Circle();
			Circle(const Rectangle<T> & aRectangle);
			
			void Set(const Vector2<T> &aCenterPosition, const T &aRadius);

			Vector2<T> myCenterPosition;
			T myRadius;
		};

		template <typename T>
		class Rectangle
		{
		public:
			Rectangle();
			Rectangle(const Circle<T> &aCircle);
			Rectangle(const Vector4<T> &aVector);

			void Set(const Vector2<T> &aStartPosition, const Vector2<T> &anEndPosition);
			inline Vector2<T> Size() const;
			inline T Width() const;
			inline T Height() const;

			Vector2<T> myStartPosition;
			Vector2<T> myEndPosition;
		};

		template <typename T>
		bool PointInsideCircle(const Circle<T> &aCircle, const Vector2<T> &aPoint);
		template <typename T>
		bool PointInsideRectangle(const Rectangle<T> &aRectangle, const Vector2<T> &aPoint);

		template <typename T>
		bool LineVsLine(const Line<T> &aFirstLine,const Line<T> &aSecondLine, Vector2<T> &anIntersectionPoint);
		template <typename T>
		bool LineVsLine(const Line<T> &aFirstLine,const Line<T> &aSecondLine);
		template <typename T>
		bool LineVsCircle(const Line<T> &aLine, const Circle<T> &aCircle, Vector2<T> &anIntersectionPoint);
		template <typename T>
		bool LineVsCircle(const Line<T> &aLine, const Circle<T> &aCircle);
		template <typename T>
		bool LineVsRectangle(const Line<T> &aLine, const Rectangle<T> &aRectangle, Vector2<T> &anIntersectionPoint);
		template <typename T>
		bool LineVsRectangle(const Line<T> &aLine, const Rectangle<T> &aRectangle);

		template <typename T>
		bool CircleVsCircle(const Circle<T> &aFirstCircle, const Circle<T> &aSecondCircle);
		template <typename T>
		bool CircleVsRectangle(const Circle<T> &aCircle, const Rectangle<T> &aRectangle);
		template <typename T>
		bool RectangleVsRectangle(const Rectangle<T> &aFirstRectangle, const Rectangle<T> &aSecondRectangle);

		template <typename T>
		Circle<T>::Circle()
		{
			myCenterPosition.Set(0,0);
			myRadius = 0;
		}
		template <typename T>
		Circle<T>::Circle(const Rectangle<T> &aRectangle)
		{
			myCenterPosition = (aRectangle.myStartPosition+aRectangle.myEndPosition)/2;
			Vector2<T> radius = (aRectangle.myEndPosition-aRectangle.myStartPosition)/2;
			myRadius = radius.Length();
		}
		template <typename T>
		void Circle<T>::Set(const Vector2<T> &aCenterPosition, const T &aRadius)
		{
			myCenterPosition = aCenterPosition;
			myRadius = aRadius;
		}

		template <typename T>
		Rectangle<T>::Rectangle()
		{
			myStartPosition.Set(0,0);
			myEndPosition.Set(0,0);
		}
		template <typename T>
		Rectangle<T>::Rectangle(const Circle<T> &aCircle)
		{
			myStartPosition.Set(aCircle.myCenterPosition.x-aCircle.myRadius, aCircle.myCenterPosition.y-aCircle.myRadius);
			myEndPosition.Set(aCircle.myCenterPosition.x+aCircle.myRadius, aCircle.myCenterPosition.y+aCircle.myRadius);
		}
		template <typename T>
		Rectangle<T>::Rectangle(const Vector4<T> &aVector)
		{
			myStartPosition.Set(aVector.x, aVector.y);
			myEndPosition.Set(aVector.z, aVector.w);
		}
		template <typename T>
		void Rectangle<T>::Set(const Vector2<T> &aStartPosition, const Vector2<T> &anEndPosition)
		{
			myStartPosition = aStartPosition;
			myEndPosition = anEndPosition;
		}
		template <typename T>
		Vector2<T> Rectangle<T>::Size() const
		{
			return myEndPosition-myStartPosition;
		}
		template <typename T>
		T Rectangle<T>::Width() const
		{
			return Size().x;
		}
		template <typename T>
		T Rectangle<T>::Height() const
		{
			return Size().y;
		}

		template <typename T>
		bool PointInsideCircle(const Circle<T> &aCircle, const Vector2<T> &aPoint)
		{
			T sqrDist = (aCircle.myCenterPosition-aPoint).LengthSqr();
			return (sqrDist<=aCircle.myRadius*aCircle.myRadius);
		}
		template <typename T>
		bool PointInsideRectangle(const Rectangle<T> &aRectangle, const Vector2<T> &aPoint)
		{
			return ((aPoint.x>=aRectangle.myStartPosition.x) && (aPoint.x<=aRectangle.myEndPosition.x) &&
				(aPoint.y>=aRectangle.myStartPosition.y) && (aPoint.y<=aRectangle.myEndPosition.y));
		}

		template <typename T>
		bool LineVsLine(const Line<T> &aFirstLine, const Line<T> &aSecondLine, Vector2<T> &anIntersectionPoint)
		{
			Vector2<T> diff = aFirstLine.point1 - aSecondLine.point1;
			Vector2<T> dir1 = aFirstLine.point2 - aFirstLine.point1;
			Vector2<T> dir2 = aSecondLine.point2 - aSecondLine.point1;
			dir1.Normalize();
			dir2.Normalize();
			T dot = dir1.Dot(dir2);
			if (abs(dot)>0)
			{
				T invDot = static_cast<T>(1) / dot;
				Vector2f diffDot(diff.Dot(dir1), diff.Dot(dir2));
				anIntersectionPoint = aFirstLine.point1 + dir1 * diffDot.x;
				return true;
			}
			return false;
		}
		template <typename T>
		bool LineVsLine(const Line<T> &aFirstLine, const Line<T> &aSecondLine)
		{
			Vector2<T> dummyPoint;
			return LineVsLine(aFirstLine, aSecondLine, dummyPoint);
		}

		template <typename T>
		bool LineVsCircle(const Line<T> &aLine, const Circle<T> &aCircle, Vector2<T> &anIntersectionPoint)
		{
			Vector2<T> m = aLine.point1 - aCircle.myCenterPosition;
			Vector2<T> len = aLine.point2-aLine.point1;
			float length = len.Length();
			if(length==0)
			{
				return false;
			}
			Vector2<T> dir = len/length;
			float c = m.Dot(m) - aCircle.myRadius*aCircle.myRadius;
			float b = m.Dot(dir);
			if((c>0.0f) && (b>0.0f))
			{
				return false;
			}
			float discr = b*b - c;
			if(discr<0.0f)
			{
				return false;
			}
			float t = -b - sqrt(discr);
			if(t<0.0f)
			{
				t = 0.0f;
			}
			if(t<=length)
			{
				anIntersectionPoint = aLine.point1 + dir * t;
				return true;
			}
			return false;
		}
		template <typename T>
		bool LineVsCircle(const Line<T> &aLine, const Circle<T> &aCircle)
		{
			Vector2<T> dummyPoint;
			return LineVsCircle(aLine, aCircle, dummyPoint);
		}

		template <typename T>
		bool LineVsRectangle(const Line<T> &aLine, const Rectangle<T> &aRectangle, Vector2<T> &anIntersectionPoint)
		{
			Vector2<T> len = aLine.point2-aLine.point1;
			float tmin = 0.0f;
			float tmax = len.Length();
			if(tmax==0)
			{
				return false;
			}
			Vector2<T> dir = len/tmax;
			if(dir.x==0)
			{
				if((aLine.point1.x<aRectangle.myStartPosition.x) || (aLine.point1.x>aRectangle.myEndPosition.x))
				{
					return false;
				}
			}
			else
			{
				float ood = 1.0f / dir.x;
				float t1 = (aRectangle.myStartPosition.x-aLine.point1.x) * ood;
				float t2 = (aRectangle.myEndPosition.x-aLine.point1.x) * ood;
				if(t1>t2)
				{
					float temp = t1;
					t1 = t2;
					t2 = temp;
				}
				if(t1>tmin)
				{
					tmin = t1;
				}
				if(t2<tmax)
				{
					tmax = t2;
				}
				if(tmin>tmax)
				{
					return false;
				}
			}
			if(dir.y==0)
			{
				if((aLine.point1.y<aRectangle.myStartPosition.y) || (aLine.point1.y>aRectangle.myEndPosition.y))
				{
					return false;
				}
			}
			else
			{
				float ood = 1.0f / dir.y;
				float t1 = (aRectangle.myStartPosition.y-aLine.point1.y) * ood;
				float t2 = (aRectangle.myEndPosition.y-aLine.point1.y) * ood;
				if(t1>t2)
				{
					float temp = t1;
					t1 = t2;
					t2 = temp;
				}
				if(t1>tmin)
				{
					tmin = t1;
				}
				if(t2<tmax)
				{
					tmax = t2;
				}
				if(tmin>tmax)
				{
					return false;
				}
			}
			anIntersectionPoint = aLine.point1 + dir * tmin;
			return true;
		}
		template <typename T>
		bool LineVsRectangle(const Line<T> &aLine, const Rectangle<T> &aRectangle)
		{
			Vector2<T> dummyPoint;
			return LineVsRectangle(aLine, aRectangle, dummyPoint);
		}

		template <typename T>
		Vector2<T> ClosestPointRectangle(const Vector2<T> &aPoint, const Rectangle<T> &aRectangle)
		{
			Vector2<T> point(aPoint);
			if(aPoint.x < aRectangle.myStartPosition.x)
			{
				point.x = aRectangle.myStartPosition.x;
			}
			else if(aPoint.x > aRectangle.myEndPosition.x)
			{
				point.x = aRectangle.myEndPosition.x;
			}

			if(aPoint.y < aRectangle.myStartPosition.y)
			{
				point.y = aRectangle.myStartPosition.y;
			}
			else if(aPoint.y > aRectangle.myEndPosition.y)
			{
				point.y = aRectangle.myEndPosition.y;
			}
			return point;
		}

		template <typename T>
		bool CircleVsCircle(const Circle<T> &aFirstCircle, const Circle<T> &aSecondCircle)
		{
			T dist = (aFirstCircle.myCenterPosition-aSecondCircle.myCenterPosition).LengthSqr();
			T radius = aFirstCircle.myRadius+aSecondCircle.myRadius;
			return (dist<=(radius*radius));
		}
		template <typename T>
		bool CircleVsRectangle(const Circle<T> &aCircle, const Rectangle<T> &aRectangle)
		{
			Vector2<T> closest = ClosestPointRectangle(aCircle.myCenterPosition, aRectangle);
			float dist = (aCircle.myCenterPosition-closest).LengthSqr();
			return (dist<=aCircle.myRadius*aCircle.myRadius);
		}
		template <typename T>
		bool RectangleVsRectangle(const Rectangle<T> &aFirstRectangle, const Rectangle<T> &aSecondRectangle)
		{
			// X vs X
			if(aFirstRectangle.myEndPosition.x < aSecondRectangle.myStartPosition.x)
				return false;
			if(aFirstRectangle.myStartPosition.x > aSecondRectangle.myEndPosition.x)
				return false;
			// Y vs Y
			if(aFirstRectangle.myEndPosition.y < aSecondRectangle.myStartPosition.y)
				return false;
			if(aFirstRectangle.myStartPosition.y > aSecondRectangle.myEndPosition.y)
				return false;

			return true;
		}
	};
};

#endif